#include "stdafx.h"
#include "BinaryStream.h"

#include <string.h>
#include <stdlib.h>
#include <algorithm>

BinaryStream::BinaryStream()
{
	rawData_.ptr_ = NULL;
	rawData_.size_ = 0;
}

BinaryStream::~BinaryStream()
{
	Clear();
}

void BinaryStream::Attach(void* ptr, uint32_t size)
{
	Clear();

	rawData_.ptr_ = ptr;
	rawData_.size_ = size;
}

void BinaryStream::Detach(void*& ptr, uint32_t& size)
{
	ptr = rawData_.ptr_;
	size = rawData_.size_;

	rawData_.ptr_ = NULL;
	rawData_.size_ = 0;
}

void BinaryStream::GetRawData(void*& ptr, uint32_t& size) const
{
	ptr = rawData_.ptr_;
	size = rawData_.size_;
}

uint32_t BinaryStream::Read(void*& p, uint32_t count)
{
	uint32_t len = Peek(p, count);
	RemoveLeft(len);

	return len;
}

uint32_t BinaryStream::Write(const void *p, uint32_t count)
{
	uint32_t write_pos = rawData_.size_;
	uint32_t new_size = rawData_.size_ + count;

	if( false == DuplicateRawData(new_size, 0, rawData_.size_) )
	{
		return 0;
	}

	memcpy((unsigned char*)rawData_.ptr_ + write_pos, (const unsigned char*)p, count);

	return count;
}

uint32_t BinaryStream::Peek(void*& p, uint32_t count) const
{
	uint32_t len = std::min<uint32_t>(rawData_.size_, count);
	memcpy(p, rawData_.ptr_, len);

	return len;
}

uint32_t BinaryStream::RemoveLeft(uint32_t count)
{
	uint32_t len = std::min<uint32_t>(rawData_.size_, count);
	uint32_t new_size = rawData_.size_ - len;

	if( false == DuplicateRawData(new_size, len, new_size) )
	{
		Clear();
		return 0;
	}

	return len;
}

uint32_t BinaryStream::RemoveRight(uint32_t count)
{
	uint32_t len = std::min<uint32_t>(rawData_.size_, count);
	uint32_t new_size = rawData_.size_ - len;

	if( false == DuplicateRawData(new_size, 0, new_size) )
	{
		Clear();
		return 0;
	}

	return len;
}

void BinaryStream::Clear()
{
	free(rawData_.ptr_);
	rawData_.ptr_ = NULL;
	rawData_.size_ = 0;
}

uint32_t BinaryStream::GetLength() const
{
	return rawData_.size_;
}

bool BinaryStream::DuplicateRawData(uint32_t new_size, uint32_t copy_offset, uint32_t copy_size)
{
	if( 0 == new_size || new_size < copy_size )
	{
		return false;
	}
	void* new_memory = malloc(new_size);

	partitionSize_ = new_size;
	memcpy((unsigned char*)new_memory, (const unsigned char*)rawData_.ptr_+copy_offset, copy_size);

	free(rawData_.ptr_);

	rawData_.ptr_ = new_memory;
	rawData_.size_ = new_size;

	return true;
}
